import ply.yacc as yacc
from verilog_lexer import tokens, lexer, beginpos_line
import logging
import os

def p_all_expr(p):
    ''' all_expr : empty
                 | module_expr all_expr 
    '''
    modules = []
    if p[1] != "":
        modules.append(p[1])
        if p[2]:
            modules.extend(p[2])
    p[0] = modules

def p_module_expr(p):
    '''module_expr : module_define_expr define_exprs ENDMODULE
    '''
    module_define_idx = 1
    end_module_idx = 3
    defines_idx = 2

    module_name, beginline, pos, inouts = p[module_define_idx]
    defines = p[defines_idx]

    endline = p.lineno(end_module_idx)

    module = Module(module_name, beginline=beginline,
                  endline=endline, pos=pos)

    for v in inouts:
        module.variables[v.name] = v

    for v in defines:
        module.variables[v.name] = v

    p[0] = module



def p_module_define_expr(p):
    '''module_define_expr : MODULE VARIABLE LPAREN args_list RPAREN SEMICOLON
    '''
    name_idx = 2
    args_idx = 4
    inouts = p[args_idx]
    p[0] = (p[name_idx], p.lineno(name_idx),
            p.lexpos(name_idx) - beginpos_line[p.lineno(name_idx)] + 1, inouts)


def p_args_list(p):
    '''args_list : empty
                 | inout_expr args_list 
    '''
    args = []
    if p[1] != '':
        if p[1]:
            args.append(p[1])
        if p[2]:
            args.extend(p[2])
    p[0] = args


def p_inout_expr(p):
    '''inout_expr : INOUT bus_expr VARIABLE bus_expr
                  | INOUT bus_expr VARIABLE bus_expr COMMA 
    '''
    name_idx = 3
    p[0] = Variable(p[name_idx],
                    p.lineno(name_idx),
                    p.lexpos(name_idx) - beginpos_line[p.lineno(name_idx)] + 1)


def p_define_exprs(p):
    '''define_exprs : empty 
                    | define_item define_exprs
    '''
    args = []
    if p[1] != '':
        if p[1]:
            args.append(p[1])
        if p[2]:
            args.extend(p[2])
    p[0] = args


def p_define_item(p):
    '''define_item : VARIABLE_TYPE bus_expr VARIABLE bus_expr SEMICOLON
    '''
    name_idx = 3
    p[0] = Variable(p[name_idx],
                    p.lineno(name_idx),
                    p.lexpos(name_idx) - beginpos_line[p.lineno(name_idx)] + 1)


def p_bus_expr(p):
    '''bus_expr : empty
                | BUSWIDTH '''


def p_empty(p):
    'empty : '
    p[0] = ''

# skip error


def p_error(p):
    parser.errok()

# 一个变量只会在module内部被使用
# 哪个文件哪一行处于哪个module中, 二分搜索
# 这一行处于哪个module中, 从这个module 中读取这个变量的信息
# 文件名（绝对路径）-> 模块字典
# 模块 -> (beginline, endline), 变量名字典
# 变量 -> 名称, 定义line,pos, [引用line,pos]


class Variable():
    def __init__(self, name: str, line=-1, pos=-1) -> None:
        self.name = name
        self.line = line
        self.pos = pos
        self.references: list = []


class Module():
    def __init__(self, name: str, beginline=-1, endline=-1, pos=-1) -> None:
        self.name = name
        self.beginline = beginline
        self.endline = endline
        self.pos = pos
        self.variables: dict[str, Variable] = {}

parser = yacc.yacc()

def find_files(folder_path):
    file_list = []
    
    for root, dirs, files in os.walk(folder_path):
        for filename in files:
            if filename.endswith('.v'): 
                file_list.append(os.path.join(root, filename))
    return file_list

def search_in_workspace(workspace: dict, filename: str, line: int, name: str) -> (None, Variable):
    if filename not in workspace:
        return None
    for module in workspace[filename]:
        if line >= module.beginline and line <= module.endline:
            if name in module.variables:
                return module.variables[name]
            else:
                return None

def parse_workspace(workspace: str) :
    files_workspace = {}
    for filename in find_files(workspace):
        with open(filename, 'r') as file:
            s = ''.join(file.readlines())
            # reset lexer lineno
            lexer.lineno = 1
            modules : [Module] = parser.parse(s, lexer=lexer)
            files_workspace[os.path.abspath(filename)] = modules
    return files_workspace


if __name__ == "__main__":
    files_workspace = {}
    for filename in find_files("test_workspace"):
        with open(filename, 'r') as file:
            s = ''.join(file.readlines())
            # reset lexer lineno
            lexer.lineno = 1
            modules : [Module] = parser.parse(s, lexer=lexer)
            for module in modules:
                print(module.name, module.beginline, module.endline, module.pos)
                for variable in module.variables:
                    v = module.variables[variable]
                    print(module.name, v.name, v.line, v.pos)
            files_workspace[os.path.abspath(filename)] = modules

    filename = os.path.abspath("./test_workspace/rtl/soc/tinyriscv_soc_top.v")
    variable = search_in_workspace(files_workspace, filename, 165, "jtag_reg_addr_i")
    if variable:
        print("searched ", variable.name, variable.line, variable.pos)
    else:
        print("None")
